/*!
 * \file cics_eci.c
 *
 * \brief ECI调用
 *
 * All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.
 *
 * \version 1.0
 * \author songbb
 * \date 2015/10/07
 *
 */

#include "conntrack.h"
#include "frct_comm.h"
#include "frreport_comm.h"

#include "cics_core.h"
#include "cics_comm.h"
#include "cics_mar.h"
#include "cics_gateway.h"
#include "cics_eci.h"

FR_STATIC char* __get_cics_rc_string(int i_cics_rc)
{
    int i = abs(i_cics_rc);
    if (i < 1000)
        return (char *) CICS_ECI_RC2STR(i);
    else
        return (char *) CICS_ECI_RC22STR(i - 1000);

}

FR_STATIC char* __get_rc_string(int i_gateway_rc
                                , int i_cics_rc)
{
    if (i_gateway_rc == 0)
        return __get_cics_rc_string(i_cics_rc);
    else
        return (char *) CICS_GATEWAY_RC2STR(i_gateway_rc);
}

FR_STATIC int __eci_req_wont_send_commarea(unsigned int ui_flow_version
                                            , unsigned int ui_call_type
                                            , unsigned int ui_extend_mode)
{
    if (ui_flow_version >= APM_CICS_GATEWAY_FLOW_V410)
        return (ui_call_type == APM_CICS_ECI_GET_SPECIFIC_REPLY ||
                ui_call_type == APM_CICS_ECI_GET_SPECIFIC_REPLY_WAIT ||
                ui_call_type == APM_CICS_ECI_GET_REPLY ||
                ui_call_type == APM_CICS_ECI_GET_REPLY_WAIT ||
                ui_extend_mode == APM_CICS_ECI_COMMIT ||
                ui_extend_mode == APM_CICS_ECI_BACKOUT) ? FR_YES : FR_NO;
    else
        return FR_NO;
}

FR_STATIC int __get_replay_returned_error(int i_cics_rc
                                          , unsigned int ui_call_type)
{
    return (i_cics_rc == 0 && (ui_call_type == APM_CICS_ECI_GET_REPLY ||
                               ui_call_type == APM_CICS_ECI_GET_REPLY_WAIT ||
                               ui_call_type == APM_CICS_ECI_GET_SPECIFIC_REPLY ||
                               ui_call_type == APM_CICS_ECI_GET_SPECIFIC_REPLY_WAIT)) ? FR_YES : FR_NO;
}


FR_STATIC int __srv_eci_req_wont_send_commarea(unsigned int ui_flow_version
                                                , unsigned int ui_call_type
                                                , unsigned int ui_extend_mode
                                                , int i_b_callback_exists
                                                , int i_cics_rc)
{
    if (ui_flow_version >= APM_CICS_GATEWAY_FLOW_V410)
        if (__get_replay_returned_error(i_cics_rc, ui_call_type) == FR_YES)
            return FR_YES;
        else
            return (ui_extend_mode == APM_CICS_ECI_COMMIT ||
                    ui_extend_mode == APM_CICS_ECI_BACKOUT ||
                    (i_b_callback_exists == FR_NO && ui_call_type == APM_CICS_ECI_ASYNC) ||
                    (i_b_callback_exists == FR_NO && ui_call_type == APM_CICS_ECI_ASYNC_TPN)) ? FR_YES : FR_NO;
    else
        return FR_NO;
}

int cics_eci_req(Conntrack *p_st_conn
                , L7session_cics *p_st_sess
                , L7_cics_packet *p_st_cics_packet
                , Frhash_header *p_st_hash_report)
{
    CICS_PRINT_INFO("cics_eci...\n");
    L7_session *p_st_session = frct_get_session(p_st_conn);
    L7session_cics_private *p_st_session_private = (L7session_cics_private *) frct_get_session_private(p_st_session);
    Frhash_header *p_hash_stmt = p_st_session_private->p_hash_stmt;
    unsigned int ui_call_type = 0;
    unsigned int ui_extend_mode = 0;
    unsigned int ui_luw_token = 0;
    unsigned int ui_message_qualifier = 0;
    int i_b_callback_exists = 0;
    unsigned int ui_server_len = 0;
    char *p_server = NULL;
    unsigned short us_tmp = 0;
    unsigned int ui_tmp = 0;
    char *p_program = NULL;
    unsigned int ui_ewlm_token_len = 0;
    unsigned int ui_commarea_len = 0;
    int i_is_commarea_outbound = 0;
    int i_is_commarea_inbound = 0;
    unsigned int ui_i = 0;
    char *p_commarea = NULL;
    L7_cics_stmt *p_st_stmt = NULL;
    Frlist_node *p_st_node = NULL;
    Frreport_body_data *p_st_body = NULL;

    if (bytebuff_skip(p_st_cics_packet->p_st_packet, p_st_cics_packet->ui_header_len) != FR_SUCC)
        goto error;

    // 为确保不重复,先删除
    frhash2_delLong(p_hash_stmt, p_st_cics_packet->ui_msg_id, frlist_node_data_free);

    if (bytebuff_get_int(p_st_cics_packet->p_st_packet, 4, &ui_call_type, BYTE_BIG_ENDIAN) != FR_SUCC)
        goto error;

    CICS_PRINT_DBG("Call_Type:[%s]\n", CICS_ECI_CALLTYPE2STR(ui_call_type));

    switch (ui_call_type)
    {
        case APM_CICS_CICS_ECILISTSYSTEMS:
            break;
        case APM_CICS_ECI_STATE_SYNC_JAVA:
        case APM_CICS_ECI_STATE_ASYNC_JAVA:
            break;
        case APM_CICS_ECI_GET_REPLY:
        case APM_CICS_ECI_GET_REPLY_WAIT:
        case APM_CICS_ECI_SYNC:
        case APM_CICS_ECI_ASYNC:
        case APM_CICS_ECI_GET_SPECIFIC_REPLY:
        case APM_CICS_ECI_GET_SPECIFIC_REPLY_WAIT:
        case APM_CICS_ECI_STATE_SYNC:
        case APM_CICS_ECI_STATE_ASYNC:
        case APM_CICS_ECI_SYNC_TPN:
        case APM_CICS_ECI_ASYNC_TPN:
            if (bytebuff_get_int(p_st_cics_packet->p_st_packet, 4, &ui_extend_mode, BYTE_BIG_ENDIAN) != FR_SUCC)
                goto error;
            if (bytebuff_get_int(p_st_cics_packet->p_st_packet, 4, &ui_luw_token, BYTE_BIG_ENDIAN) != FR_SUCC)
                goto error;
            if (bytebuff_get_int(p_st_cics_packet->p_st_packet, 4, &ui_message_qualifier, BYTE_BIG_ENDIAN) != FR_SUCC)
                goto error;
            if (cics_mar_get_boolean(p_st_cics_packet, &i_b_callback_exists) != FR_SUCC)
                goto error;
            /**
             * TODO: 这里关于Server字段有两种编码方式,ctgclient的Java源码中也未按照flow_version作区分,现在只能以现有已知的版本写逻辑;
             * 情况不清楚的情况下,再做猜测.
             */
            if (p_st_cics_packet->ui_flow_version <= APM_CICS_GATEWAY_FLOW_V501)
            {
                if (cics_mar_get_string(p_st_sess, p_st_cics_packet, 8, &p_server) != FR_SUCC)
                    goto error;
            }
            else if (p_st_cics_packet->ui_flow_version >= APM_CICS_GATEWAY_FLOW_V710)
            {
                if (bytebuff_get_int(p_st_cics_packet->p_st_packet, 4, &ui_server_len, BYTE_BIG_ENDIAN) != FR_SUCC)
                    goto error;
                if (ui_server_len > 0)
                {
                    if (cics_mar_get_string(p_st_sess, p_st_cics_packet, ui_server_len, &p_server) != FR_SUCC)
                        goto error;
                }
            }
            else
            {
                if (bytebuff_peek_short(p_st_cics_packet->p_st_packet, 2, &us_tmp, BYTE_BIG_ENDIAN) != FR_SUCC)
                    goto error;
                if (bytebuff_get_int(p_st_cics_packet->p_st_packet, 4, &ui_server_len, BYTE_BIG_ENDIAN) != FR_SUCC)
                    goto error;
                if (us_tmp > 0)
                {
                    if (bytebuffer_backspace(p_st_cics_packet->p_st_packet, 4) != FR_SUCC)
                        goto error;
                    if (cics_mar_get_string(p_st_sess, p_st_cics_packet, 8, &p_server) != FR_SUCC)
                        goto error;
                }
                else if (us_tmp == 0 && ui_server_len > 0)
                {
                    if (cics_mar_get_string(p_st_sess, p_st_cics_packet, ui_server_len, &p_server) != FR_SUCC)
                        goto error;
                }
                else // us_tmp == 0 && ui_server_len == 0
                {
                    if (bytebuff_get_int(p_st_cics_packet->p_st_packet, 4, &ui_tmp, BYTE_BIG_ENDIAN) != FR_SUCC)
                        goto error;
                    if (ui_tmp > 0)
                    {
                        if (bytebuffer_backspace(p_st_cics_packet->p_st_packet, 4) != FR_SUCC)
                            goto error;
                    }
                }
            }
            CICS_PRINT_ALERT("server:[%s]\n", p_server);

            cics_free((void **) &(p_st_sess->p_userid));
            if (cics_mar_get_string(p_st_sess, p_st_cics_packet, 16, &(p_st_sess->p_userid)) != FR_SUCC)
                goto error;
            if (p_st_sess->p_userid != NULL)
                CICS_PRINT_ALERT("userid:[%s]\n", p_st_sess->p_userid);

            if (bytebuff_skip(p_st_cics_packet->p_st_packet, 16) != FR_SUCC) // Password
                goto error;
            if (cics_mar_get_string(p_st_sess, p_st_cics_packet, 8, &p_program) != FR_SUCC)
                goto error;
            CICS_PRINT_ALERT("program:[%s]\n", p_program);
            if (bytebuff_skip(p_st_cics_packet->p_st_packet, 4) != FR_SUCC) // Transid
                goto error;
            /**
             * TODO: 发现V710版本中有ewlmToken字段,但现在源码版本不完整,不确定这个字段是从哪个版本开始的
             */
            if (p_st_cics_packet->ui_flow_version >= APM_CICS_GATEWAY_FLOW_V710)
            {
                if (bytebuff_get_int(p_st_cics_packet->p_st_packet, 4, &ui_ewlm_token_len, BYTE_BIG_ENDIAN) != FR_SUCC)
                    goto error;
                if (ui_ewlm_token_len > 0)
                {
                    if (bytebuff_skip(p_st_cics_packet->p_st_packet, ui_ewlm_token_len) != FR_SUCC) // ewlmToken
                        goto error;
                }
            }
            if (bytebuff_get_int(p_st_cics_packet->p_st_packet, 4, &ui_commarea_len, BYTE_BIG_ENDIAN) != FR_SUCC)
                goto error;
            if (cics_mar_get_boolean(p_st_cics_packet, &i_is_commarea_outbound) != FR_SUCC)
                goto error;
            if (i_is_commarea_outbound == FR_YES)
            {
                if (bytebuff_get_int(p_st_cics_packet->p_st_packet, 4, &ui_i, BYTE_BIG_ENDIAN) != FR_SUCC)
                    goto error;
            }
            else
                ui_i = ui_commarea_len;
            if (cics_mar_get_boolean(p_st_cics_packet, &i_is_commarea_inbound) != FR_SUCC)
                goto error;
            if (i_is_commarea_inbound == FR_YES)
            {
                if (bytebuff_skip(p_st_cics_packet->p_st_packet, 4) != FR_SUCC) // iCommareaLengthIn
                    goto error;
            }
            if (ui_i > 0)
            {
                if (__eci_req_wont_send_commarea(p_st_cics_packet->ui_flow_version, ui_call_type, ui_extend_mode) == FR_NO)
                {
                    if (cics_mar_get_string(p_st_sess, p_st_cics_packet, ui_i, &p_commarea) != FR_SUCC)
                        goto error;
                    CICS_PRINT_ALERT("commarea:[%s]\n", p_commarea);
                }
            }

            /**
             * XXX: channel数据未做解析
             */

            if ((p_st_stmt = (L7_cics_stmt *) frmalloc(sizeof(L7_cics_stmt))) == NULL)
            {
                CICS_MALLOCK_ERR;
                goto error;
            }
            memset(p_st_stmt, 0x00, sizeof(L7_cics_stmt));
            p_st_stmt->ui_msg_id = p_st_cics_packet->ui_msg_id;
            p_st_stmt->ui_extend_mode = ui_extend_mode;
            p_st_stmt->i_b_callback_exists = i_b_callback_exists;

            p_st_node = frlist_node_create(p_st_stmt);
            if (frhash2_addLong(p_hash_stmt, p_st_cics_packet->ui_msg_id, p_st_node) != FR_SUCC)
                goto error;

            // 新建上报数据结构体
            if ((p_st_body = frreport_body_new_l7(p_st_conn)) == NULL)
                goto error;

            if (p_program && frreport_body_l7_add_str(p_st_body, APM_REPORT_KEY_TRANS_TITLE, p_program) != FR_SUCC)
                goto error;
            if (p_program && frreport_body_l7_add_str(p_st_body, APM_DTS_REPORT_REQ_SERVICE_METHOD, p_program) != FR_SUCC)
                goto error;
            if (ui_luw_token != 0 && frreport_body_l7_add_uint(p_st_body, APM_REPORT_KEY_TRANS_RELATED_ID, ui_luw_token) != FR_SUCC)
                goto error;
            if (p_server && frreport_body_l7_add_str(p_st_body, APM_REPORT_KEY_SESSION_SERVER, p_server) != FR_SUCC)
                goto error;
            if (p_commarea)
            {
                if (frreport_body_l7_add_uint(p_st_body, APM_DTS_REPORT_REQ_ARG_SIZE, 1) != FR_SUCC)
                    goto error;
                if (frreport_body_l7_add_str(p_st_body, APM_DTS_REPORT_REQ_ARG_DATA, p_commarea) != FR_SUCC)
                    goto error;
            }

            // 上报数据
            if (cics_report_msg_data_req(p_st_conn, p_st_sess, p_st_cics_packet, p_st_body, p_st_hash_report) != FR_SUCC)
                goto error;

            break;
        default:
            ;
    }
    /**
     * XXX: sECITimeout,autoMsgQual,getCTGXid,ctgCorrelator未做解析
     */

    cics_free((void **) &p_program);
    cics_free((void **) &p_commarea);
    cics_free((void **) &p_server);
    return FR_SUCC;
error:
    cics_free((void **) &p_server);
    cics_free((void **) &p_program);
    cics_free((void **) &p_commarea);
    frreport_body_free(&p_st_body);
    return FR_FAIL;
}

int cics_eci_resp(Conntrack *p_st_conn
                , L7session_cics *p_st_sess
                , L7_cics_packet *p_st_cics_packet
                , Frhash_header *p_st_hash_report)
{
    CICS_PRINT_INFO("cics_eci_resp...\n");
    L7_session *p_st_session = frct_get_session(p_st_conn);
    L7session_cics_private *p_st_session_private = (L7session_cics_private *) frct_get_session_private(p_st_session);
    Frhash_header *p_hash_stmt = p_st_session_private->p_hash_stmt;
    unsigned int ui_call_type = 0;
    unsigned int ui_luw_token = 0;
    unsigned int ui_message_qualifier = 0;
    int i_cics_rc = 0;
    char *p_abend_code = NULL;
    unsigned int ui_commarea_len = 0;
    int i_flag2 = 0;
    int i_flag3 = 0;
    unsigned int ui_i = 0;
    L7_cics_stmt *p_st_stmt = NULL;
    Frlist_node *p_st_node = NULL;
    char *p_commarea = NULL;
    char *p_rc_string = NULL;
    Frreport_body_data *p_st_body = NULL;

    if (bytebuff_skip(p_st_cics_packet->p_st_packet, p_st_cics_packet->ui_header_len) != FR_SUCC)
        goto error;

    if (bytebuff_get_int(p_st_cics_packet->p_st_packet, 4, &ui_call_type, BYTE_BIG_ENDIAN) != FR_SUCC)
        goto error;

    CICS_PRINT_DBG("Call_Type:[%s]\n", CICS_ECI_CALLTYPE2STR(ui_call_type));

    switch (ui_call_type)
    {
        case APM_CICS_CICS_ECILISTSYSTEMS:
            break;
        case APM_CICS_ECI_STATE_SYNC_JAVA:
        case APM_CICS_ECI_STATE_ASYNC_JAVA:
            break;
        case APM_CICS_ECI_GET_REPLY:
        case APM_CICS_ECI_GET_REPLY_WAIT:
        case APM_CICS_ECI_SYNC:
        case APM_CICS_ECI_ASYNC:
        case APM_CICS_ECI_GET_SPECIFIC_REPLY:
        case APM_CICS_ECI_GET_SPECIFIC_REPLY_WAIT:
        case APM_CICS_ECI_STATE_SYNC:
        case APM_CICS_ECI_STATE_ASYNC:
        case APM_CICS_ECI_SYNC_TPN:
        case APM_CICS_ECI_ASYNC_TPN:
            if ((p_st_node = frhash2_getLong(p_hash_stmt, p_st_cics_packet->ui_msg_id)) == NULL)
                goto error;
            p_st_stmt = (L7_cics_stmt *) FRLIST_GET_NODE_DATA(p_st_node);

            // 新建上报数据结构体
            if ((p_st_body = frreport_body_new_l7(p_st_conn)) == NULL)
                goto error;

            if (bytebuff_get_int(p_st_cics_packet->p_st_packet, 4, &ui_luw_token, BYTE_BIG_ENDIAN) != FR_SUCC)
                goto error;
            if (bytebuff_get_int(p_st_cics_packet->p_st_packet, 4, &ui_message_qualifier, BYTE_BIG_ENDIAN) != FR_SUCC)
                goto error;
            if (bytebuff_get_int(p_st_cics_packet->p_st_packet, 4, (unsigned int *) &i_cics_rc, BYTE_BIG_ENDIAN) != FR_SUCC)
                goto error;
            CICS_PRINT_ALERT("cics_rc:[%d]\n", i_cics_rc);
            if (cics_mar_get_string(p_st_sess, p_st_cics_packet, 4, &p_abend_code) != FR_SUCC)
                goto error;
            CICS_PRINT_ALERT("abend_code:[%s]\n", p_abend_code);
            cics_free((void **) &p_abend_code);
            if (bytebuff_get_int(p_st_cics_packet->p_st_packet, 4, &ui_commarea_len, BYTE_BIG_ENDIAN) != FR_SUCC)
                goto error;
            if (cics_mar_get_boolean(p_st_cics_packet, &i_flag2) != FR_SUCC)
                goto error;
            if (i_flag2 == FR_YES)
            {
                if (cics_mar_get_boolean(p_st_cics_packet, &i_flag3) != FR_SUCC)
                    goto error;
                if (bytebuff_get_int(p_st_cics_packet->p_st_packet, 4, &ui_i, BYTE_BIG_ENDIAN) != FR_SUCC)
                    goto error;
            }
            else
                ui_i = ui_commarea_len;
            if (__srv_eci_req_wont_send_commarea(p_st_cics_packet->ui_flow_version,
                                                 ui_call_type,
                                                 p_st_stmt->ui_extend_mode,
                                                 p_st_stmt->i_b_callback_exists,
                                                 i_cics_rc) == FR_NO)
            {
                if (cics_mar_get_string(p_st_sess, p_st_cics_packet, ui_i, &p_commarea) != FR_SUCC)
                    goto error;
                CICS_PRINT_ALERT("commarea:[%s]\n", p_commarea);
                if (p_commarea)
                {
                    if (frreport_body_l7_add_uint(p_st_body, APM_DTS_REPORT_RESP_ARG_SIZE, 1) != FR_SUCC)
                        goto error;
                    if (frreport_body_l7_add_str(p_st_body, APM_DTS_REPORT_RESP_ARG_DATA, p_commarea) != FR_SUCC)
                        goto error;
                }
            }

            /**
             * XXX: channel数据未做解析
             */

            if (frreport_body_l7_add_int(p_st_body, APM_REPORT_KEY_RESP_CODE, i_cics_rc) != FR_SUCC)
                goto error;
            p_rc_string = __get_rc_string(p_st_cics_packet->i_gateway_rc, i_cics_rc);
            if (p_rc_string != NULL)
            {
                if (frreport_body_l7_add_str(p_st_body, APM_REPORT_KEY_RESP_CODE_STRING, p_rc_string) != FR_SUCC)
                    goto error;
            }

            // 上报数据
            if (cics_report_msg_data_resp(p_st_conn, p_st_sess, p_st_cics_packet, p_st_body, p_st_hash_report) != FR_SUCC)
                goto error;

            frhash2_delLong(p_hash_stmt, p_st_cics_packet->ui_msg_id, frlist_node_data_free);

            break;
        default:
            ;
    }

    cics_free((void **) &p_commarea);

    return FR_SUCC;
error:
    cics_free((void **) &p_abend_code);
    cics_free((void **) &p_commarea);
    frreport_body_free(&p_st_body);
    return FR_FAIL;
}
